package com.solomon.travel.common.util;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
 * 
 * @author Nan 2015-11
 */
public class HttpClientUtil {
	
	private static PoolingHttpClientConnectionManager cm;
	private static String EMPTY_STR = "";
	private static String UTF_8 = "UTF-8";
	 
	private static void init() {
		if (cm == null) {
			cm = new PoolingHttpClientConnectionManager();
			cm.setMaxTotal(30);
			cm.setDefaultMaxPerRoute(15);
		}
	}
	
	
	private static CloseableHttpClient getHttpClient() {
		 init();
		 HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
	            public boolean retryRequest(IOException exception,
	                    int executionCount, HttpContext context) {
	                if (executionCount >= 3) {
	                    return false;
	                }
	                if (exception instanceof NoHttpResponseException) {
	                    return true;
	                }
	                if (exception instanceof SSLHandshakeException) {
	                    return false;
	                }
	                if (exception instanceof InterruptedIOException) {
	                    return false;
	                }
	                if (exception instanceof UnknownHostException) {
	                    return false;
	                }
	                if (exception instanceof ConnectTimeoutException) {
	                    return false;
	                }
	                if (exception instanceof SSLException) {
	                    return false;
	                }

	                HttpClientContext clientContext = HttpClientContext
	                        .adapt(context);
	                HttpRequest request = clientContext.getRequest();
	                // 如果请求是幂等的，就再次尝试
	                if (!(request instanceof HttpEntityEnclosingRequest)) {
	                    return true;
	                }
	                return false;
	            }
	        };

		return HttpClients.custom()
				.setConnectionManager(cm)
				.setRetryHandler(httpRequestRetryHandler)
				.build();
	}

	public static String httpGetRequest(String url) {
		HttpGet httpGet = new HttpGet(url);
		return getResult(httpGet);
	}

	public static String httpGetRequest(String url, Map<String, Object> params) throws URISyntaxException {
		URIBuilder ub = new URIBuilder();
		ub.setPath(url);

		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		ub.setParameters(pairs);

		HttpGet httpGet = new HttpGet(ub.build());
		return getResult(httpGet);
	}

	public static String httpGetRequest(String url, Map<String, Object> headers, Map<String, Object> params)
			throws URISyntaxException {
		URIBuilder ub = new URIBuilder();
		ub.setPath(url);

		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		ub.setParameters(pairs);
		
		HttpGet httpGet = new HttpGet(ub.build());
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}
		return getResult(httpGet);
	}
	
	public static  Map<String, Object> getRequest(String url, Map<String, Object> headers, Map<String, Object> params)
			throws URISyntaxException {
		URIBuilder ub = new URIBuilder();
		ub.setPath(url);

		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		ub.setParameters(pairs);

		HttpGet httpGet = new HttpGet(ub.build());
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}
		return getResponse(httpGet);
	}

	public static String httpPostRequest(String url) {
		HttpPost httpPost = new HttpPost(url);
		return getResult(httpPost);
	}

	public static String httpPostRequest(String url, Map<String, Object> params) throws UnsupportedEncodingException {
		HttpPost httpPost = new HttpPost(url);
		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
		return getResult(httpPost);
	}

	public static String httpPostRequest(String url, Map<String, Object> headers, Map<String, Object> params)
			throws UnsupportedEncodingException {
		HttpPost httpPost = new HttpPost(url);
		
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}

		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));

		return getResult(httpPost);
	}
	
	public static Map<String, Object> postRequest(String url, Map<String, Object> headers, Map<String, Object> params) 
			throws UnsupportedEncodingException{
		HttpPost httpPost = new HttpPost(url);
		
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}

		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));

		return getResponse(httpPost);
	}
	
	public static String httpPutRequest(String url,Map<String, Object> headers,Map<String, Object> params)throws Exception{
		HttpPut httpPut = new HttpPut(url);
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpPut.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}

		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		httpPut.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));

		return getResult(httpPut);
	}
	
	public static Map<String, Object> putRequest(String url,Map<String, Object> headers,Map<String, Object> params)throws Exception{
		HttpPut httpPut = new HttpPut(url);
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpPut.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}

		ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
		httpPut.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));

		return getResponse(httpPut);
	}
	
	public static Map<String, Object> deleteRequest(String url,Map<String, Object> headers,Map<String, Object> params)throws Exception{
		url+= "?";
		for (Map.Entry<String, Object> param : params.entrySet()) {
			url += param.getKey() + "=" + param.getValue()+"&";
		}
		url = url.substring(0, url.length()-1);
		HttpDelete HttpDelete = new HttpDelete(url);
		for (Map.Entry<String, Object> header : headers.entrySet()) {
			HttpDelete.addHeader(header.getKey(), String.valueOf(header.getValue()));
		}
		return getResponse(HttpDelete);
	}
	
	public static Map<String, Object> postJsonRequest(String url,Map<String, Object> headers,String json) throws ParseException, IOException{
		HttpPost httpPost = new HttpPost(url);	
		httpPost.addHeader("Content-Type","application/json");
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}
		StringEntity strEntity = new StringEntity(json, UTF_8);      
	    httpPost.setEntity(strEntity); 
	    return getResponse(httpPost);
	}
	
	public static Map<String, Object> putJsonRequest(String url,Map<String, Object> headers,String json) throws ParseException, IOException{
		HttpPut httpPut = new HttpPut(url);
		httpPut.addHeader("Content-Type","application/json");
		for (Map.Entry<String, Object> param : headers.entrySet()) {
			httpPut.addHeader(param.getKey(), String.valueOf(param.getValue()));
		}
		StringEntity strEntity = new StringEntity(json, UTF_8);      
		httpPut.setEntity(strEntity); 
	    return getResponse(httpPut);
	}
	


	private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
		ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
		for (Map.Entry<String, Object> param : params.entrySet()) {
			pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
		}

		return pairs;
	}
	
	private static Map<String, Object> getResponse(HttpRequestBase request) {
		CloseableHttpClient httpClient = getHttpClient();
		RequestConfig requestConfig = RequestConfig.custom()
										.setConnectTimeout(3000)
										.setConnectionRequestTimeout(3000)
										.setSocketTimeout(3000)
										.build();
		request.setConfig(requestConfig);
		Map<String, Object> result = new HashMap<>();
		try {
			CloseableHttpResponse response = httpClient.execute(request);
			Integer code = response.getStatusLine().getStatusCode();
			String body = null;
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				body = EntityUtils.toString(entity,Charset.forName("UTF-8"));
				response.close();
			}
			result.put("code", code);
			result.put("data", body);
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {

		}
		return result;
	}
	
	private static String getResult(HttpRequestBase request) {
		CloseableHttpClient httpClient = getHttpClient();
		RequestConfig requestConfig = RequestConfig.custom()
										.setConnectTimeout(15000)
										.setConnectionRequestTimeout(15000)
										.setSocketTimeout(15000)
										.build();
		request.setConfig(requestConfig);
		try {
			CloseableHttpResponse response = httpClient.execute(request);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String result = EntityUtils.toString(entity,Charset.forName("UTF-8"));
				response.close();
				return result;
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {

		}

		return EMPTY_STR;
	}

}